//! Pre-startup OS-a, sredi neke stvari prije nego ides na "visu" razinu

#define _ARH_SPREMNIK_C_
#include "spremnik.h"

//GDT - sadrzi opisnike segmenata
static gdt_t gdt[] = {
	GDT_0,				// 0. - prazan (nule)
	GDT_INSTRUKCIJE_JEZGRE,		// 1. - instrukcije jezgre
	GDT_PODACI_JEZGRE,		// 2. - podaci jezgre
	GDT_TSS,			// 3. - opisnik TSS-a
	GDT_INSTRUKCIJE_PROGRAMA,	// 4. - intrukcije procesa
	GDT_PODACI_PROGRAMA		// 5. - podaci procesa
};

//task state segment:
//koristi se samo jedan, samo elementi ss0 i esp0 za koristenje pri prekidu
static tss_t tss = {
	//kad se dogodi prekid, koristi ovaj stog
	.ss0 = GDT_OPISNIK(SEGJ_PODACI, GDT, RAZINA_JEZGRE)
};

//vrijednosti spremnickih lokacija za programe
extern char programi_pocetak, programi_velicina, kraj_svega;

void arh_inicijalizacija_segmenata ()
{
	gdtr_t gdtr;
	gdtr.gdt = gdt;
	gdtr.limit = sizeof(gdt) - 1;

	//azuriraj TSS zapis u GDTu
	arh_azuriraj_opisnik_segmenta(SEG_TSS, &tss, sizeof(tss_t) - 1, 0);

	//segmenti za jezgru su vec postavljeni i azurni (osim gornje granice)
	//azuriraj zapis za korisnicke segmente u GDTu
	arh_azuriraj_opisnik_segmenta(SEG_INSTR,
		(void *) arh_pomak(), (uint32) &kraj_svega, 3);
	arh_azuriraj_opisnik_segmenta(SEG_PODACI, 
		(void *) arh_pomak(), (uint32) &kraj_svega, 3);

	//postavi adresu GDT tablice u registar
	__asm__ ("lgdt	%0\n\t" :: "m" (gdtr) );
		
	//azuriraj segmentne registre za jezgru
	__asm__ __volatile__ (
			"ljmp	%0, $1f	\n"	//azuriraj "cs"
		"1:				\n\t"
			"mov	%1, %%eax	\n\t"
			"mov	%%ax, %%ds	\n\t"
			"mov	%%ax, %%es	\n\t"
			"mov	%%ax, %%fs	\n\t"
			"mov	%%ax, %%gs	\n\t"
			"mov	%%ax, %%ss	\n\t"
			:: "i" (GDT_OPISNIK(SEGJ_INSTR, GDT, RAZINA_JEZGRE)),
			"i" (GDT_OPISNIK(SEGJ_PODACI, GDT, RAZINA_JEZGRE))
			: "memory"
		);

	//postavi opisnik TSSa u registar
	__asm__ ("ltrw %w0\n\t"::"r"(GDT_OPISNIK(SEG_TSS, GDT, RAZINA_JEZGRE)));
}



void arh_azuriraj_tss_esp0 (void *esp0)
{
	tss.esp0 = (uint32) esp0;

}

//azurira segmente sa pocetnom adresom, velicinom i razinom
static void arh_azuriraj_opisnik_segmenta (int id, void *poc_adr, uint32 vel,
					   int razina)
{
	uint32 addr = (uint32) poc_adr;
	uint32 v = vel;

	gdt[id].base_addr0 =  addr & 0x0000ffff;
	gdt[id].base_addr1 = (addr & 0x00ff0000) >> 16;
	gdt[id].base_addr2 = (addr & 0xff000000) >> 24;

	if (vel < (1 << 20)) { //vel < 1 MB?
		v = vel - 1;
		gdt[id].G = 0; //granularnost 1 B
	}
	else { //granularnost 4 KB
		v = vel >> 12;
		if (vel & 0x0fff)
			v++;
		v--;
		gdt[id].G = 1;
	}
	gdt[id].segm_limit0 =  v & 0x0000ffff;
	gdt[id].segm_limit1 = (v & 0x000f0000) >> 16;

	gdt[id].DPL = razina;
}

/*
//testiranje
static void arh_ispisi_gdt_zapis(int id)
{
	gdt_t *g = &gdt[id];

	j_ispisi("%x %d %d %d %d %x %d %d %d %d %x %x %x\n",
		g->base_addr2,
		g->G,
		g->DB,
		g->L,
		g->AVL,
		g->segm_limit1,
		g->P,
		g->DPL,
		g->S,
		g->type,
		g->base_addr1,
		g->base_addr0,
		g->segm_limit0);

}
*/